feat(plugin): Add entry_command field to PluginManifest#2230
feat(plugin): Add entry_command field to PluginManifest#2230jpshackelford wants to merge 2 commits intomainfrom
Conversation
Add an optional entry_command field to PluginManifest that allows plugin
authors to specify which command should be invoked by default when
launching their plugin.
Changes:
- Add entry_command field to PluginManifest (types.py)
- Add entry_slash_command property to Plugin class (plugin.py)
- Add entry_command field to MarketplacePluginEntry and update
to_plugin_manifest() to pass it through
- Add comprehensive tests for the new functionality
Example usage in plugin.json:
{
"name": "city-weather",
"version": "1.0.0",
"entry_command": "now"
}
The Plugin.entry_slash_command property returns the full slash command
(e.g., '/city-weather:now') or None if no entry_command is defined.
Co-authored-by: openhands <openhands@all-hands.dev>
API breakage checks (Griffe)Result: Passed |
enyst
left a comment
There was a problem hiding this comment.
Just a quick note. I'm a little confused why isn't this the first user message. Or a field of the first user message, e.g. an additional TextContent field.
It seems like it behaves the same as the user triggering a command-like skill themselves, by writing, e.g. "OK now start a conversation on the API to ask for the /weather in Tokio"... maybe?
|
One other design question related to this, plugins already support commands. Would using this already-existing mechanism suffice for this use case? Or is there some other functionality that is needed that is not yet included in commands. I'm just thinking about whether we can keep the schema simple. |
|
@neubig This assumes the existing support of commands. What it provides in addition is that it allows a plugin to declare which of the commands should be called when we are using the in plugin in an auto-start launch scenario. The auto-start launch scenario probably doesn't apply to CLI, except perhaps in a headless case, but it is relevant for Cloud when customers are building complex solutions that involve many skills but which are kicked-off via REST API and do not expose the conversation to end-users. |
|
@enyst You are correct that it would be more flexible to have a plugin declare an initial message rather than an entry point command but constraining it to the command does two things:
|
Agent server REST API breakage checks (OpenAPI)Result: Passed |
No, sorry, we would not load up the initial message with content of a command / skill file. Just the command, not its content: e.g.
That's all they should need, right? They don't need to insert the contents of the "/weather" skill. Instead, we already do automatically insert the contents? Edited to add: TBH that might be a bad example. Maybe the way commands should work is that it starts the message. Like people typically expect them, e.g. "/review but be nice okay?" |
|
If I understand correctly, and please correct me if not, what this PR would do is that:
But we inserted "/weather" in the plugin source, so we execute "/weather" even if the user wanted the time? |
That would be the flow for a normal conversation and you could use the initial message that is already part of the Cloud API for that--though I suppose we'd need to add some logic in another PR that would register the entry-point command with just the What I DID have in mind is to allow a plugin to launch with no initial message at the press of a button and have the agent start working. |
Sure, please let me elaborate, I don't think I meant differently, sorry for the confusing examples. 🤔 My point is, any conversation has an initial message. I mean, the LLM request/responses array always needs an initial message
So, it seems to me, at the press of a button, the application should insert "/weather" or "/weather:now" or whatever it likes, in the place of the initial message, instead of opening the conversation tab with an input field waiting for the user. It just starts. From the user perspective, it's exactly "I pressed a button, the conversation just started, and it does weather things" From the backend perspective, the conversation started with the initial message "/weather" or "/weather:now", so it does what is supposed to do in that case. Please tell if I misunderstand? |
Exactly. So we are on the same page. So in an auto-executing plugin scenario, some system will construct the initial message from the plugin metadata. I am arguing we don't need the plugin to declare an initial message if it has commands and can declare the entry-point command. That way the message is always part of a skill and not part of a new "initial message" plugin metadata which creates ambiguity for newly initiated plugin authors. |
enyst
left a comment
There was a problem hiding this comment.
I think the issue here is that of course some system (a client application) can send "/weather:now", as initial message, regardless that the weather plugin never specified any initial message nor entry command.
In that case, the plugin itself is not auto-executing, strictly speaking, but the entire system client+sdk does auto-execute plugins at a button press. 🤔
That said, I don't feel strongly about it. I understand you really like the concept of auto-executing plugins, and I think we may well try it. (I think Joe was calling it somewhere "autostart", the autostart equivalent of this CC plugin concept)
So I'm fine with this if @neubig is ok with trying it.
Please allow me to express here quickly what's haunting me. We had .md microagents long before CC existed, even, not to mention its Agentskills; and we had python Agentskills (python scripts for the agent to call) long before, too. "Long" in LLM-lifetimes, of which we had several since 2024 😅
I know microagents were useful to people; I'm not fully sure agentskills were. Then CC came along and combined them. They just, like, combined them. Named them Agentskills; declared them open standard. Suddenly, it's like, it clicked; a lot of users find them incredibly helpful and they pop up all over the place.
There are some caveats, of course; but this is humbling. I really think nobody knows the right UX for agents. The right things that will both feel intuitive to humans AND allow agentic workflows that feel - if not are - more suitable for real world tasks than before. Maybe this one is one of them. Idk. Worth trying! So thank you for pushing it forward.
|
[Automatic Post]: It has been a while since there was any activity on this PR. @jpshackelford, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up. |
1 similar comment
|
[Automatic Post]: It has been a while since there was any activity on this PR. @jpshackelford, are you still working on it? If so, please go ahead, if not then please request review, close it, or request that someone else follow up. |
Summary
Add an optional
entry_commandfield toPluginManifestthat allows plugin authors to specify which command should be invoked by default when launching their plugin.Closes #2229
Changes
PluginManifest(intypes.py): Addedentry_commandfield with descriptionPlugin(inplugin.py): Addedentry_slash_commandproperty that returns the full slash command (e.g.,/city-weather:now) orNoneif no entry_command is definedMarketplacePluginEntry(intypes.py): Addedentry_commandfield and updatedto_plugin_manifest()to preserve it when converting marketplace entries to manifestsExample Usage
plugin.json:
{ "name": "city-weather", "version": "1.0.0", "description": "Get current weather for any city", "entry_command": "now" }Consumer code:
Design Rationale
Primary Consumer: Plugin Launch UI
The
entry_commandfield enables the OpenHands Cloud UI for launching plugins to construct the initial message when users click "Launch" on a plugin:entry_commandand constructs the initial message/launch?message=/city-weather:now%20Tokyoinitial_messageThe
entry_slash_commandproperty encapsulates the/<plugin-name>:<command>format so external consumers don't need to know the slash command syntax.Other Possible Consumers
Why
entry_commandinstead of a free-form initial message?Constraining to a command reference (rather than arbitrary message content) provides two benefits:
Enforces good practices - Prevents plugin authors from loading up the initial message with content that belongs in a command or skill file. Better to constrain initially and widen flexibility later if needed.
Enables clean multi-plugin handling - When multiple plugins specify auto-start entry points, expressing behavior in terms of commands makes it easier to define invocation semantics (serial, parallel, subagents, etc.).
Relationship to existing commands
This builds on the existing
commands/directory support. Plugins already define commands;entry_commandsimply declares which one should be invoked by default in headless/launch scenarios.For the full plugin launch architecture, see Plugin Launch Flow.
Claude Code Compatibility
This is an OpenHands extension to the Claude Code plugin format. The
entry_commandfield is not part of the official Claude Code schema. However, this should not break compatibility because:PluginManifestalready usesextra="allow"which preserves unknown fieldsChecklist
Agent Server images for this PR
• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server
Variants & Base Images
eclipse-temurin:17-jdknikolaik/python-nodejs:python3.12-nodejs22golang:1.21-bookwormPull (multi-arch manifest)
# Each variant is a multi-arch manifest supporting both amd64 and arm64 docker pull ghcr.io/openhands/agent-server:c0dee6c-pythonRun
All tags pushed for this build
About Multi-Architecture Support
c0dee6c-python) is a multi-arch manifest supporting both amd64 and arm64c0dee6c-python-amd64) are also available if needed